<!doctype html>
<html lang="zh-CN">
<head>
	<meta charset="utf-8">
	<title>Laravel - 为 WEB 艺术家创造的 PHP 框架。 | Laravel 中文网</title>
	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
	<meta name="author" content="Laravel 中文网">
	<meta name="description" content="Laravel - 为 WEB 艺术家创造的 PHP 框架。| Laravel 中文网">
	<meta name="keywords" content="Laravel中文社区,php框架,laravel中文网,php framework,restful routing,laravel,laravel php">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<!--[if lte IE 9]>
		<script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
	<![endif]-->
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link rel="stylesheet" href="../../assets/css/laravel.css">
</head>
<body class="docs language-php">

	<span class="overlay"></span>

	<nav class="main">
		<div class="container">
			<a href="../../index.html" class="brand">
				<img src="../../assets/img/laravel-logo.png" height="30">
				Laravel
			</a>

			<div class="responsive-sidebar-nav">
				<a href="#" class="toggle-slide menu-link btn">&#9776;</a>
			</div>

				<div class="switcher">
					<div class="dropdown">
						<button class="btn dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-expanded="true">
							<!--<span class="faint">v</span> -->
							5.0
							<span class="caret"></span>
						</button>
						<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
								<li role="presentation">
									<a role="menuitem" tabindex="-1" href="index.html">5.0</a>
								</li>
								<li role="presentation">
									<a role="menuitem" tabindex="-1" href="../4.2/index.html">4.2</a>
								</li>
								<li role="presentation">
									<a role="menuitem" tabindex="-1" href="../4.1/index.html">4.1</a>
								</li>
								<li role="presentation">
									<a role="menuitem" tabindex="-1" href="../4.0/index.html">4.0</a>
								</li>
						</ul>
					</div>
				</div>

			<ul class="main-nav">
				<li class="nav-docs"><a href="../index.html">中文文档</a></li>
				<li class="nav-community"><a href="http://wenda.golaravel.com" target="_blank">问答社区</a></li>
				<li class="nav-api"><a href="http://laravel.com/api/5.0/" target="_blank">API</a></li>
				<li class="nav-laracasts"><a href="https://laracasts.com" target="_blank">视频教程（国外）</a></li>
				<li class="nav-forge"><a href="https://forge.laravel.com" target="_blank">Forge</a></li>
				
			</ul>
		</div>
	</nav>

		<nav id="slide-menu" class="slide-menu" role="navigation">
		
		<div class="brand">
			<a href="../../index.html">
				<img src="../../assets/img/laravel-logo-white.png" height="50">
			</a>
		</div>

		<ul class="slide-main-nav">
			<li><a href="../../index.html">首页</a></li>
			<li class="nav-docs"><a href="../index.html">中文文档</a></li>
			<li class="nav-community"><a href="http://wenda.golaravel.com" target="_blank">问答社区</a></li>
			<li class="nav-api"><a href="http://laravel.com/api/5.0/" target="_blank">API</a></li>
			<li class="nav-laracasts"><a href="https://laracasts.com" target="_blank">视频教程（国外）</a></li>
			<li class="nav-forge"><a href="https://forge.laravel.com" target="_blank">Forge</a></li>
			
		</ul>

		<div class="slide-docs-nav">
			<h2>文档目录</h2>
			<ul>
<li>前言
<ul>
<li><a href="releases.html">发行说明</a></li>
<li><a href="upgrade.html">升级向导</a></li>
<li><a href="contributions.html">贡献向导</a></li>
</ul>
</li>
<li>环境配置
<ul>
<li><a href="installation.html">安装</a></li>
<li><a href="configuration.html">配置</a></li>
<li><a href="homestead.html">Homestead</a></li>
</ul>
</li>
<li>基本功能
<ul>
<li><a href="routing.html">路由</a></li>
<li><a href="middleware.html">中间件</a></li>
<li><a href="controllers.html">控制器</a></li>
<li><a href="requests.html">请求</a></li>
<li><a href="responses.html">响应</a></li>
<li><a href="views.html">视图</a></li>
</ul>
</li>
<li>系统架构
<ul>
<li><a href="providers.html">服务提供者</a></li>
<li><a href="container.html">服务容器</a></li>
<li><a href="contracts.html">Contracts</a></li>
<li><a href="facades.html">Facades</a></li>
<li><a href="lifecycle.html">请求的生命周期</a></li>
<li><a href="structure.html">应用程序结构</a></li>
</ul>
</li>
<li>系统服务
<ul>
<li><a href="authentication.html">认证</a></li>
<li><a href="billing.html">交易</a></li>
<li><a href="cache.html">缓存</a></li>
<li><a href="collections.html">集合</a></li>
<li><a href="bus.html">Command Bus</a></li>
<li><a href="extending.html">核心扩展</a></li>
<li><a href="elixir.html">Elixir</a></li>
<li><a href="encryption.html">加密</a></li>
<li><a href="envoy.html">Envoy 任务执行器</a></li>
<li><a href="errors.html">错误与日志</a></li>
<li><a href="events.html">事件</a></li>
<li><a href="filesystem.html">文件系统与云存储</a></li>
<li><a href="hashing.html">哈希</a></li>
<li><a href="helpers.html">辅助方法</a></li>
<li><a href="localization.html">本地化</a></li>
<li><a href="mail.html">邮件</a></li>
<li><a href="packages.html">扩展包开发</a></li>
<li><a href="pagination.html">分页</a></li>
<li><a href="queues.html">队列</a></li>
<li><a href="session.html">会话</a></li>
<li><a href="templates.html">模板</a></li>
<li><a href="testing.html">单元测试</a></li>
<li><a href="validation.html">表单验证</a></li>
</ul>
</li>
<li>数据库
<ul>
<li><a href="database.html">基本用法</a></li>
<li><a href="queries.html">查询构造器</a></li>
<li><a href="eloquent.html">Eloquent ORM</a></li>
<li><a href="schema.html">结构生成器</a></li>
<li><a href="migrations.html">迁移与数据填充</a></li>
<li><a href="redis.html">Redis</a></li>
</ul>
</li>
<li>Artisan 命令行工具
<ul>
<li><a href="artisan.html">概览</a></li>
<li><a href="commands.html">开发</a></li>
</ul>
</li>
</ul>

		</div>

	</nav>

	<div class="docs-wrapper container">

		<section class="sidebar">
			<ul>
<li>前言
<ul>
<li><a href="releases.html">发行说明</a></li>
<li><a href="upgrade.html">升级向导</a></li>
<li><a href="contributions.html">贡献向导</a></li>
</ul>
</li>
<li>环境配置
<ul>
<li><a href="installation.html">安装</a></li>
<li><a href="configuration.html">配置</a></li>
<li><a href="homestead.html">Homestead</a></li>
</ul>
</li>
<li>基本功能
<ul>
<li><a href="routing.html">路由</a></li>
<li><a href="middleware.html">中间件</a></li>
<li><a href="controllers.html">控制器</a></li>
<li><a href="requests.html">请求</a></li>
<li><a href="responses.html">响应</a></li>
<li><a href="views.html">视图</a></li>
</ul>
</li>
<li>系统架构
<ul>
<li><a href="providers.html">服务提供者</a></li>
<li><a href="container.html">服务容器</a></li>
<li><a href="contracts.html">Contracts</a></li>
<li><a href="facades.html">Facades</a></li>
<li><a href="lifecycle.html">请求的生命周期</a></li>
<li><a href="structure.html">应用程序结构</a></li>
</ul>
</li>
<li>系统服务
<ul>
<li><a href="authentication.html">认证</a></li>
<li><a href="billing.html">交易</a></li>
<li><a href="cache.html">缓存</a></li>
<li><a href="collections.html">集合</a></li>
<li><a href="bus.html">Command Bus</a></li>
<li><a href="extending.html">核心扩展</a></li>
<li><a href="elixir.html">Elixir</a></li>
<li><a href="encryption.html">加密</a></li>
<li><a href="envoy.html">Envoy 任务执行器</a></li>
<li><a href="errors.html">错误与日志</a></li>
<li><a href="events.html">事件</a></li>
<li><a href="filesystem.html">文件系统与云存储</a></li>
<li><a href="hashing.html">哈希</a></li>
<li><a href="helpers.html">辅助方法</a></li>
<li><a href="localization.html">本地化</a></li>
<li><a href="mail.html">邮件</a></li>
<li><a href="packages.html">扩展包开发</a></li>
<li><a href="pagination.html">分页</a></li>
<li><a href="queues.html">队列</a></li>
<li><a href="session.html">会话</a></li>
<li><a href="templates.html">模板</a></li>
<li><a href="testing.html">单元测试</a></li>
<li><a href="validation.html">表单验证</a></li>
</ul>
</li>
<li>数据库
<ul>
<li><a href="database.html">基本用法</a></li>
<li><a href="queries.html">查询构造器</a></li>
<li><a href="eloquent.html">Eloquent ORM</a></li>
<li><a href="schema.html">结构生成器</a></li>
<li><a href="migrations.html">迁移与数据填充</a></li>
<li><a href="redis.html">Redis</a></li>
</ul>
</li>
<li>Artisan 命令行工具
<ul>
<li><a href="artisan.html">概览</a></li>
<li><a href="commands.html">开发</a></li>
</ul>
</li>
</ul>

		</section>

		<article>
			<h1>Eloquent ORM</h1>
<ul>
<li><a href="#introduction">介绍</a></li>
<li><a href="#basic-usage">基本用法</a></li>
<li><a href="#mass-assignment">批量赋值</a></li>
<li><a href="#insert-update-delete">新增，修改，删除</a></li>
<li><a href="#soft-deleting">软删除</a></li>
<li><a href="#timestamps">时间戳</a></li>
<li><a href="#query-scopes">范围查询</a></li>
<li><a href="#global-scopes">Global Scopes</a></li>
<li><a href="#relationships">关联</a></li>
<li><a href="#querying-relations">关联查询</a></li>
<li><a href="#eager-loading">预载入</a></li>
<li><a href="#inserting-related-models">新增关联模型</a></li>
<li><a href="#touching-parent-timestamps">更新上层模型时间戳</a></li>
<li><a href="#working-with-pivot-tables">操作枢纽表</a></li>
<li><a href="#collections">集合</a></li>
<li><a href="#accessors-and-mutators">获取器和修改器</a></li>
<li><a href="#date-mutators">日期转换器</a></li>
<li><a href="#attribute-casting">属性类型转换</a></li>
<li><a href="#model-events">模型事件</a></li>
<li><a href="#model-observers">模型观察者</a></li>
<li><a href="#model-url-generation">模型 URL 生成</a></li>
<li><a href="#converting-to-arrays-or-json">转换数组 / JSON</a></li>
</ul>
<p><a name="introduction"></a></p>
<h2>介绍</h2>
<p>Laravel 的 Eloquent ORM 提供了漂亮、简洁的 ActiveRecord 实现来和数据库的互动。 每个数据库表会和一个对应的「模型」互动。</p>
<p>在开始之前，记得把 <code>config/database.php</code> 里的数据库连接配置好。</p>
<p><a name="basic-usage"></a></p>
<h2>基本用法</h2>
<p>我们先从建立一个 Eloquent 模型开始。模型通常放在 <code>app</code> 目录下，但是您可以将它们放在任何地方，只要能通过 composer.json 自动载入。所有的 Eloquent 模型都继承于 <code>Illuminate\Database\Eloquent\Model</code> 。</p>
<h4>定义一个 Eloquent 模型</h4>
<pre><code>class User extends Model {}
</code></pre>
<p>你也可以通过 <code>make:model</code> 命令自动生成 Eloquent 模型：</p>
<pre><code>php artisan make:model User
</code></pre>
<p>注意我们并没有告诉 Eloquent User 模型会使用哪个数据库表。若没有特别指定，系统会默认自动对应名称为「类名称的小写复数形态」的数据库表。所以，在上面的例子中， Eloquent 会假设 <code>User</code> 模型将把数据存在 <code>users</code> 数据库表。您也可以在类中定义 <code>table</code> 属性自定义要对应的数据库表。</p>
<pre><code>class User extends Model {

    protected $table = 'my_users';

}
</code></pre>
<blockquote>
<p><strong>注意：</strong> Eloquent 也会假设每个数据库表都有一个字段名称为 <code>id</code> 的主键。您可以在类里定义 <code>primaryKey</code> 属性来重写。同样的，您也可以定义 <code>connection</code> 属性，指定模型连接到指定的数据库连接。</p>
</blockquote>
<p>定义好模型之后，您就可以从数据库表新增及获取数据了。注意在默认情况下，在数据库表里需要有 <code>updated_at</code> 和 <code>created_at</code> 两个字段。如果您不想设定或自动更新这两个字段，则将类里的 <code>$timestamps</code> 属性设为 false即可。</p>
<h4>取出所有模型数据</h4>
<pre><code>$users = User::all();
</code></pre>
<h4>根据主键取出一条数据</h4>
<pre><code>$user = User::find(1);

var_dump($user-&gt;name);
</code></pre>
<blockquote>
<p><strong>提示：</strong> 所有<a href="queries.html">查询构造器</a>里的方法，查询 Eloquent 模型时也可以使用。</p>
</blockquote>
<h4>根据主键取出一条数据或抛出异常</h4>
<p>有时, 您可能想要在找不到模型数据时抛出异常，通过 <code>App::error</code> 捕捉异常处理并显示 404 页面。</p>
<pre><code>$model = User::findOrFail(1);

$model = User::where('votes', '&gt;', 100)-&gt;firstOrFail();
</code></pre>
<p>要注册错误处理，可以监听 <code>ModelNotFoundException</code></p>
<pre><code>use Illuminate\Database\Eloquent\ModelNotFoundException;

App::error(function(ModelNotFoundException $e)
{
    return Response::make('Not Found', 404);
});
</code></pre>
<h4>Eloquent 模型结合查询语法</h4>
<pre><code>$users = User::where('votes', '&gt;', 100)-&gt;take(10)-&gt;get();

foreach ($users as $user)
{
    var_dump($user-&gt;name);
}
</code></pre>
<h4>Eloquent 聚合查询</h4>
<p>当然，您也可以使用查询构造器的聚合查询方法。</p>
<pre><code>$count = User::where('votes', '&gt;', 100)-&gt;count();
</code></pre>
<p>如果没办法使用流畅接口产生出查询语句，也可以使用 <code>whereRaw</code> 方法：</p>
<pre><code>$users = User::whereRaw('age &gt; ? and votes = 100', array(25))-&gt;get();
</code></pre>
<h4>拆分查询</h4>
<p>如果您要处理非常多（数千条）Eloquent 查询结果，使用 <code>chunk</code> 方法可以让您顺利工作而不会消耗大量内存：</p>
<pre><code>User::chunk(200, function($users)
{
    foreach ($users as $user)
    {
        //
    }
});
</code></pre>
<p>传到方法里的第一个参数表示每次「拆分」要取出的数据数量。第二个参数的闭合函数会在每次取出数据时被调用。</p>
<h4>指定查询时连接数据库</h4>
<p>您也可以指定在执行 Eloquent 查询时要使用哪个数据库连接。只要使用 <code>on</code> 方法：</p>
<pre><code>$user = User::on('connection-name')-&gt;find(1);
</code></pre>
<p>如果您在使用 <a href="database.html#read-write-connections">读取 / 写入连接</a>, 您可以通过如下命令来强制查询使用 <code>写入</code> 连接：</p>
<pre><code>$user = User::onWriteConnection()-&gt;find(1);
</code></pre>
<p><a name="mass-assignment"></a></p>
<h2>批量赋值</h2>
<p>在建立一个新的模型时，您把属性以数组的方式传入模型的构造方法，这些属性值会经由批量赋值存成模型数据。这一点非常方便，然而，若盲目地将用户输入存到模型时，可能会造成<strong>严重的</strong>安全隐患。如果盲目的存入用户输入，用户可以随意的修改<strong>任何</strong>以及<strong>所有</strong>模型的属性。基于这个理由，所有的 Eloquent 模型默认会阻止批量赋值 。</p>
<p>我们以在模型里设定 <code>fillable</code> 或 <code>guarded</code> 属性作为开始。</p>
<h4>定义模型 <code>Fillable</code> 属性</h4>
<p><code>fillable</code> 属性指定了哪些字段支持批量赋值 。可以设定在类的属性里或是实例化后设定。</p>
<pre><code>class User extends Model {

    protected $fillable = array('first_name', 'last_name', 'email');

}
</code></pre>
<p>在上面的例子里，只有三个属性允许批量赋值。</p>
<h4>定义模型 <code>Guarded</code> 属性</h4>
<p><code>guarded</code> 与 <code>fillable</code> 相反，是作为「黑名单」而不是「白名单」：</p>
<pre><code>class User extends Model {

    protected $guarded = array('id', 'password');

}
</code></pre>
<blockquote>
<p><strong>注意：</strong> 使用 <code>guarded</code> 时， <code>Input::get()</code> 或任何用户可以控制的未过滤数据，永远不应该传入 <code>save</code> 或 <code>update</code> 方法，因为没有在「黑名单」内的字段可能被更新。</p>
</blockquote>
<h4>阻挡所有属性被批量赋值</h4>
<p>上面的例子中， <code>id</code> 和 <code>password</code> 属性<strong>不会</strong>被批量赋值，而所有其他的属性则允许批量赋值。您也可以使用 guard 属性阻止所有属性被批量赋值：</p>
<pre><code>protected $guarded = array('*');
</code></pre>
<p><a name="insert-update-delete"></a></p>
<h2>新增，更新，删除</h2>
<p>要从模型新增一条数据到数据库，只要建立一个模型实例并调用 <code>save</code> 方法即可。</p>
<h4>储存新的模型数据</h4>
<pre><code>$user = new User;

$user-&gt;name = 'John';

$user-&gt;save();
</code></pre>
<blockquote>
<p><strong>注意：</strong> 通常 Eloquent 模型主键值会自动递增。但是您若想自定义主键，将 <code>incrementing</code> 属性设成 false 。</p>
</blockquote>
<p>也可以使用 <code>create</code> 方法存入新的模型数据，新增完后会返回新增的模型实例。但是在新增前，需要先在模型类里设定好 <code>fillable</code> 或 <code>guarded</code> 属性，因为 Eloquent 默认会防止批量赋值。</p>
<p>在新模型数据被储存或新增后，若模型有自动递增主键，可以从对象取得 <code>id</code> 属性值：</p>
<pre><code>$insertedId = $user-&gt;id;
</code></pre>
<h4>在模型里设定 Guarded 属性</h4>
<pre><code>class User extends Model {

    protected $guarded = array('id', 'account_id');

}
</code></pre>
<h4>使用模型的 Create 方法</h4>
<pre><code>// 在数据库中建立一个新的用户...
$user = User::create(array('name' =&gt; 'John'));

// 以属性找用户，若没有则新增并取得新的实例...
$user = User::firstOrCreate(array('name' =&gt; 'John'));

// 以属性找用户，若没有则建立新的实例...
$user = User::firstOrNew(array('name' =&gt; 'John'));
</code></pre>
<h4>更新取出的模型</h4>
<p>要更新模型，可以取出它，更改属性值，然后使用 <code>save</code> 方法：</p>
<pre><code>$user = User::find(1);

$user-&gt;email = 'john@foo.com';

$user-&gt;save();
</code></pre>
<h4>储存模型和关联数据</h4>
<p>有时您可能不只想要储存模型本身，也想要储存关联的数据。您可以使用 <code>push</code> 方法达到目的：</p>
<pre><code>$user-&gt;push();
</code></pre>
<p>您可以结合查询语句，批次更新模型：</p>
<pre><code>$affectedRows = User::where('votes', '&gt;', 100)-&gt;update(array('status' =&gt; 2));
</code></pre>
<blockquote>
<p>**注意： ** 若使用 Eloquent 查询构造器批次更新模型，则不会触发模型事件。</p>
</blockquote>
<h4>删除模型</h4>
<p>要删除模型，只要使用实例调用 <code>delete</code> 方法：</p>
<pre><code>$user = User::find(1);

$user-&gt;delete();
</code></pre>
<h4>按主键值删除模型</h4>
<pre><code>User::destroy(1);

User::destroy(array(1, 2, 3));

User::destroy(1, 2, 3);
</code></pre>
<p>当然，您也可以结合查询语句批次删除模型：</p>
<pre><code>$affectedRows = User::where('votes', '&gt;', 100)-&gt;delete();
</code></pre>
<h4>只更新模型的时间戳</h4>
<p>如果您只想要更新模型的时间戳，您可以使用 <code>touch</code> 方法：</p>
<pre><code>$user-&gt;touch();
</code></pre>
<p><a name="soft-deleting"></a></p>
<h2>软删除</h2>
<p>通过软删除方式删除了一个模型后，模型中的数据并不是真的从数据库被移除。而是会设定 <code>deleted_at</code>时间戳。要让模型使用软删除功能，只要在模型类里加入 <code>SoftDeletingTrait</code> 即可：</p>
<pre><code>use Illuminate\Database\Eloquent\SoftDeletes;

class User extends Model {

    use SoftDeletes;

    protected $dates = ['deleted_at'];

}
</code></pre>
<p>要加入 <code>deleted_at</code> 字段到数据库表，可以在迁移文件里使用 s<code>oftDeletes</code> 方法：</p>
<pre><code>$table-&gt;softDeletes();
</code></pre>
<p>现在当您使用模型调用 <code>delete</code> 方法时， <code>deleted_at</code>字段会被更新成现在的时间戳。在查询使用软删除功能的模型时，被「删除」的模型数据不会出现在查询结果里。</p>
<h4>强制查询软删除数据</h4>
<p>要强制让已被软删除的模型数据出现在查询结果里，在查询时使用 <code>withTrashed</code> 方法：</p>
<pre><code>$users = User::withTrashed()-&gt;where('account_id', 1)-&gt;get();
</code></pre>
<p><code>withTrashed</code> 也可以用在关联查询：</p>
<pre><code>$user-&gt;posts()-&gt;withTrashed()-&gt;get();
</code></pre>
<p>如果您只想查询被软删除的模型数据，可以使用 <code>onlyTrashed</code> 方法：</p>
<pre><code>$users = User::onlyTrashed()-&gt;where('account_id', 1)-&gt;get();
</code></pre>
<p>要把被软删除的模型数据恢复，使用 <code>restore</code> 方法：</p>
<pre><code>$user-&gt;restore();
</code></pre>
<p>您也可以结合查询语句使用 <code>restore</code> ：</p>
<pre><code>User::withTrashed()-&gt;where('account_id', 1)-&gt;restore();
</code></pre>
<p>如同 <code>withTrashed</code> ， <code>restore</code> 方法也可以用在关联对象：</p>
<pre><code>$user-&gt;posts()-&gt;restore();
</code></pre>
<p>如果想要真的从模型数据库删除，使用 <code>forceDelete</code> 方法：</p>
<pre><code>$user-&gt;forceDelete();
</code></pre>
<p><code>forceDelete</code> 方法也可以用在关联对象：</p>
<pre><code>$user-&gt;posts()-&gt;forceDelete();
</code></pre>
<p>要确认模型是否被软删除了，可以使用 <code>trashed</code> 方法：</p>
<pre><code>if ($user-&gt;trashed())
{
    //
}
</code></pre>
<p><a name="timestamps"></a></p>
<h2>时间戳</h2>
<p>默认 Eloquent 会自动维护数据库表的 <code>created_at</code> 和 <code>updated_at</code> 字段。只要把这两个「时间戳」字段加到数据库表， Eloquent 就会处理剩下的工作。如果不想让 Eloquent 自动维护这些字段，把下面的属性加到模型类里：</p>
<h4>关闭自动更新时间戳</h4>
<pre><code>class User extends Model {

    protected $table = 'users';

    public $timestamps = false;

}
</code></pre>
<h4>自定义时间戳格式</h4>
<p>如果想要自定义时间戳格式，可以在模型类里重写 <code>getDateFormat</code> 方法：</p>
<pre><code>class User extends Model {

    protected function getDateFormat()
    {
        return 'U';
    }

}
</code></pre>
<p><a name="query-scopes"></a></p>
<h2>范围查询</h2>
<h4>定义范围查询</h4>
<p>范围查询可以让您轻松的重复利用模型的查询逻辑。要设定范围查询，只要定义有 <code>scope</code> 前缀的模型方法：</p>
<pre><code>class User extends Model {

    public function scopePopular($query)
    {
        return $query-&gt;where('votes', '&gt;', 100);
    }

    public function scopeWomen($query)
    {
        return $query-&gt;whereGender('W');
    }

}
</code></pre>
<h4>使用范围查询</h4>
<pre><code>$users = User::popular()-&gt;women()-&gt;orderBy('created_at')-&gt;get();
</code></pre>
<h4>动态范围查询</h4>
<p>有时您可能想要定义可接受参数的范围查询方法。只要把参数加到方法里：</p>
<pre><code>class User extends Model {

    public function scopeOfType($query, $type)
    {
        return $query-&gt;whereType($type);
    }

}
</code></pre>
<p>然后把参数值传到范围查询方法调用里：</p>
<pre><code>$users = User::ofType('member')-&gt;get();
</code></pre>
<p><a name="global-scopes"></a></p>
<h2>Global Scopes</h2>
<p>有时您可能希望定义一个 scope 可以用于模型的所有查询中。本质上，这也是 Eloquent 的&quot;软删除&quot;功能的实现原理。Global scopes 是通过 PHP traits 的组合以及实现 <code>Illuminate\Database\Eloquent\ScopeInterface</code> 接口来定义的。</p>
<p>首先，我们需要定义一个 trait。 这里我们用 Laravel 的 <code>SoftDeletes</code> 举例：</p>
<pre><code>trait SoftDeletes {

    /**
     * Boot the soft deleting trait for a model.
     *
     * @return void
     */
    public static function bootSoftDeletes()
    {
        static::addGlobalScope(new SoftDeletingScope);
    }

}
</code></pre>
<p>如果一个 Eloquent 模型引入了一个 trait ，而这个 trait 中带有符合 <code>bootNameOfTrait</code> 惯例命名的方法 ,那么这个方法会在 Eloquent 模型启动的时候调用，
您可以在此时注册 global scope ，或者做一些其他您想要的操作。定义的 scope 必须实现 <code>ScopeInterface</code> 接口，这个接口提供了两个方法：<code>apply</code> 和 <code>remove</code>。</p>
<p><code>apply</code> 方法接受一个 <code>Illuminate\Database\Eloquent\Builder</code> 查询构造器对象以及它所应用的 <code>Model</code>，用来添加这个 scope 所需的额外的 <code>where</code> 子句。而<code>remove</code> 方法同样接受一个 <code>Builder</code> 对象以及 <code>Model</code> ，用来反向的执行 <code>apply</code> 操作。也就是说，<code>remove</code> 方法应该移除已经添加的 <code>where</code> 子句 (或者其他查询子句)。因此，我们的 <code>SoftDeletingScope</code> 的方法应该如下：</p>
<pre><code>/**
 * Apply the scope to a given Eloquent query builder.
 *
 * @param  \Illuminate\Database\Eloquent\Builder  $builder
 * @return void
 */
public function apply(Builder $builder, Model $model)
{
    $model = $builder-&gt;getModel();

    $builder-&gt;whereNull($model-&gt;getQualifiedDeletedAtColumn());
}

/**
 * Remove the scope from the given Eloquent query builder.
 *
 * @param  \Illuminate\Database\Eloquent\Builder  $builder
 * @return void
 */
public function remove(Builder $builder, Model $model)
{
    $column = $model-&gt;getQualifiedDeletedAtColumn();

    $query = $builder-&gt;getQuery();

    foreach ((array) $query-&gt;wheres as $key =&gt; $where)
    {
        // If the where clause is a soft delete date constraint, we will remove it from
        // the query and reset the keys on the wheres. This allows this developer to
        // include deleted model in a relationship result set that is lazy loaded.
        if ($this-&gt;isSoftDeleteConstraint($where, $column))
        {
            unset($query-&gt;wheres[$key]);

            $query-&gt;wheres = array_values($query-&gt;wheres);
        }
    }
}
</code></pre>
<p><a name="relationships"></a></p>
<h2>关联</h2>
<p>当然，您的数据库表很可能跟另一张表相关联。例如，一篇 blog 文章可能有很多评论，或是一张订单跟下单客户相关联。 Eloquent 让管理和处理这些关联变得很容易。 Laravel 有很多种关联类型：</p>
<ul>
<li><a href="#one-to-one">一对一</a></li>
<li><a href="#one-to-many">一对多</a></li>
<li><a href="#many-to-many">多对多</a></li>
<li><a href="#has-many-through">远层一对多关联</a></li>
<li><a href="#polymorphic-relations">多态关联</a></li>
<li><a href="#many-to-many-polymorphic-relations">多态的多对多关联</a></li>
</ul>
<p><a name="one-to-one"></a></p>
<h3>一对一</h3>
<h4>定义一对一关联</h4>
<p>一对一关联是很基本的关联。例如一个 <code>User</code> 模型会对应到一个 <code>Phone</code> 。 在 Eloquent 里可以像下面这样定义关联：</p>
<pre><code>class User extends Model {

    public function phone()
    {
        return $this-&gt;hasOne('App\Phone');
    }

}
</code></pre>
<p>传到 <code>hasOne</code> 方法里的第一个参数是关联模型的类名称。定义好关联之后，就可以使用 Eloquent 的<a href="#dynamic-properties">动态属性</a>取得关联对象：</p>
<pre><code>$phone = User::find(1)-&gt;phone;
</code></pre>
<p>SQL 会执行如下语句：</p>
<pre><code>select * from users where id = 1

select * from phones where user_id = 1
</code></pre>
<p>注意， Eloquent 假设对应的关联模型数据库表里，外键名称是基于模型名称。在这个例子里，默认 <code>Phone</code> 模型数据库表会以 <code>user_id</code> 作为外键。如果想要更改这个默认，可以传入第二个参数到 <code>hasOne</code> 方法里。更进一步，您可以传入第三个参数，指定关联的外键要对应到本身的哪个字段：</p>
<pre><code>return $this-&gt;hasOne('App\Phone', 'foreign_key');

return $this-&gt;hasOne('App\Phone', 'foreign_key', 'local_key');
</code></pre>
<h4>定义相对的关联</h4>
<p>要在 <code>Phone</code> 模型里定义相对的关联，可以使用 <code>belongsTo</code> 方法：</p>
<pre><code>class Phone extends Model {

    public function user()
    {
        return $this-&gt;belongsTo('App\User');
    }

}
</code></pre>
<p>在上面的例子里， Eloquent 默认会使用 <code>phones</code> 数据库表的 <code>user_id</code> 字段查询关联。如果想要自己指定外键字段，可以在 <code>belongsTo</code> 方法里传入第二个参数：</p>
<pre><code>class Phone extends Model {

    public function user()
    {
        return $this-&gt;belongsTo('App\User', 'local_key');
    }

}
</code></pre>
<p>除此之外，也可以传入第三个参数指定要参照上层数据库表的哪个字段：</p>
<pre><code>class Phone extends Model {

    public function user()
    {
        return $this-&gt;belongsTo('App\User', 'local_key', 'parent_key');
    }

}
</code></pre>
<p><a name="one-to-many"></a></p>
<h3>一对多</h3>
<p>一对多关联的例子如，一篇 Blog 文章可能「有很多」评论。可以像这样定义关联：</p>
<pre><code>class Post extends Model {

    public function comments()
    {
        return $this-&gt;hasMany('App\Comment');
    }

}
</code></pre>
<p>现在可以经由<a href="#dynamic-properties">动态属性</a>取得文章的评论：</p>
<pre><code>$comments = Post::find(1)-&gt;comments;
</code></pre>
<p>如果需要增加更多条件限制，可以在调用 <code>comments</code> 方法后面通过链式查询条件方法：</p>
<pre><code>$comments = Post::find(1)-&gt;comments()-&gt;where('title', '=', 'foo')-&gt;first();
</code></pre>
<p>同样的，您可以传入第二个参数到 <code>hasMany</code> 方法更改默认的外键名称。以及，如同 <code>hasOne</code> 关联，可以指定本身的对应字段：</p>
<pre><code>return $this-&gt;hasMany('App\Comment', 'foreign_key');

return $this-&gt;hasMany('App\Comment', 'foreign_key', 'local_key');
</code></pre>
<h4>定义相对的关联</h4>
<p>要在 <code>Comment</code> 模型定义相对应的关联，可使用 <code>belongsTo</code> 方法：</p>
<pre><code>class Comment extends Model {

    public function post()
    {
        return $this-&gt;belongsTo('App\Post');
    }

}
</code></pre>
<p><a name="many-to-many"></a></p>
<h3>多对多</h3>
<p>多对多关联更为复杂。这种关联的例子如，一个用户（ user ）可能用有很多身份（ role ），而一种身份可能很多用户都有。例如很多用户都是「管理者」。多对多关联需要用到三个数据库表： <code>users</code> ， <code>roles</code> ，和 <code>role_user</code> 。 <code>role_user</code> 枢纽表命名是以相关联的两个模型数据库表，依照字母顺序命名，枢纽表里面应该要有 <code>user_id</code> 和 <code>role_id</code> 字段。</p>
<p>可以使用 <code>belongsToMany</code> 方法定义多对多关系：</p>
<pre><code>class User extends Model {

    public function roles()
    {
        return $this-&gt;belongsToMany('App\Role');
    }

}
</code></pre>
<p>现在我们可以从 <code>User</code> 模型取得 roles：</p>
<pre><code>$roles = User::find(1)-&gt;roles;
</code></pre>
<p>如果不想使用默认的枢纽数据库表命名方式，可以传递数据库表名称作为 <code>belongsToMany</code> 方法的第二个参数：</p>
<pre><code>return $this-&gt;belongsToMany('App\Role', 'user_roles');
</code></pre>
<p>也可以更改默认的关联字段名称：</p>
<pre><code>return $this-&gt;belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id');
</code></pre>
<p>当然，也可以在 <code>Role</code> 模型定义相对的关联：</p>
<pre><code>class Role extends Model {

    public function users()
    {
        return $this-&gt;belongsToMany('App\User');
    }

}
</code></pre>
<p><a name="has-many-through"></a></p>
<h3>Has Many Through 远层一对多关联</h3>
<p>「远层一对多关联」提供了方便简短的方法，可以经由多层间的关联取得远层的关联。例如，一个 <code>Country</code> 模型可能通过 <code>Users</code> 关联到很多 <code>Posts</code> 模型。 数据库表间的关系可能看起来如下：</p>
<pre><code>countries
    id - integer
    name - string

users
    id - integer
    country_id - integer
    name - string

posts
    id - integer
    user_id - integer
    title - string
</code></pre>
<p>虽然 <code>posts</code> 数据库表本身没有 <code>country_id</code> 字段，但 <code>hasManyThrough</code> 方法让我们可以使用 <code>$country-&gt;posts</code> 取得 country 的 posts。我们可以定义以下关联：</p>
<pre><code>class Country extends Model {

    public function posts()
    {
        return $this-&gt;hasManyThrough('App\Post', 'User');
    }

}
</code></pre>
<p>如果想要手动指定关联的字段名称，可以传入第三和第四个参数到方法里：</p>
<pre><code>class Country extends Model {

    public function posts()
    {
        return $this-&gt;hasManyThrough('App\Post', 'User', 'country_id', 'user_id');
    }

}
</code></pre>
<p><a name="polymorphic-relations"></a></p>
<h3>多态关联</h3>
<p>多态关联可以用一个简单的关联方法，就让一个模型同时关联多个模型。例如，您可能想让 photo 模型同时和一个 staff 或 order 模型关联。可以定义关联如下：</p>
<pre><code>class Photo extends Model {

    public function imageable()
    {
        return $this-&gt;morphTo();
    }

}

class Staff extends Model {

    public function photos()
    {
        return $this-&gt;morphMany('App\Photo', 'imageable');
    }

}

class Order extends Model {

    public function photos()
    {
        return $this-&gt;morphMany('App\Photo', 'imageable');
    }

}
</code></pre>
<h4>取得多态关联对象</h4>
<p>现在我们可以从 staff 或 order 模型取得多态关联对象：</p>
<pre><code>$staff = Staff::find(1);

foreach ($staff-&gt;photos as $photo)
{
    //
}
</code></pre>
<h4>取得多态关联对象的拥有者</h4>
<p>然而，多态关联真正神奇的地方，在于要从 <code>Photo</code> 模型取得 staff 或 order 对象时：</p>
<pre><code>$photo = Photo::find(1);

$imageable = $photo-&gt;imageable;
</code></pre>
<p>Photo 模型里的 <code>imageable</code> 关联会返回 <code>Staff</code> 或 <code>Order</code> 实例，取决于这是哪一种模型拥有的照片。</p>
<h4>多态关联的数据库表结构</h4>
<p>为了理解多态关联的运作机制，来看看它们的数据库表结构：</p>
<pre><code>staff
    id - integer
    name - string

orders
    id - integer
    price - integer

photos
    id - integer
    path - string
    imageable_id - integer
    imageable_type - string
</code></pre>
<p>要注意的重点是 <code>photos</code> 数据库表的 <code>imageable_id</code> 和 <code>imageable_type</code>。在上面的例子里， ID 字段会包含 staff 或 order 的 ID，而 type 是拥有者的模型类名称。这就是让 ORM 在取得 <code>imageable</code> 关联对象时，决定要哪一种模型对象的机制。</p>
<p><a name="many-to-many-polymorphic-relations"></a></p>
<h3>多态的多对多关联</h3>
<h4>Polymorphic Many To Many Relation Table Structure 多态的多对多关联数据库表结构</h4>
<p>除了一般的多态关联，也可以使用多对多的多态关联。例如，Blog 的 <code>Post</code> 和 <code>Video</code> 模型可以共用多态的 <code>Tag</code> 关联模型。首先，来看看数据库表结构：</p>
<pre><code>posts
    id - integer
    name - string

videos
    id - integer
    name - string

tags
    id - integer
    name - string

taggables
    tag_id - integer
    taggable_id - integer
    taggable_type - string
</code></pre>
<p>现在，我们准备好设定模型关联了。 <code>Post</code> 和 <code>Video</code> 模型都可以经由 <code>tags</code> 方法建立 <code>morphToMany</code> 关联：</p>
<pre><code>class Post extends Model {

    public function tags()
    {
        return $this-&gt;morphToMany('App\Tag', 'taggable');
    }

}
</code></pre>
<p>在 <code>Tag</code> 模型里针对每一种关联建立一个方法：</p>
<pre><code>class Tag extends Model {

    public function posts()
    {
        return $this-&gt;morphedByMany('App\Post', 'taggable');
    }

    public function videos()
    {
        return $this-&gt;morphedByMany('App\Video', 'taggable');
    }

}
</code></pre>
<p><a name="querying-relations"></a></p>
<h2>关联查询</h2>
<h4>根据关联条件查询</h4>
<p>在取得模型数据时，您可能想要以关联模型作为查询限制。例如，您可能想要取得所有「至少有一篇评论」的Blog 文章。可以使用 <code>has</code> 方法达成目的：</p>
<pre><code>$posts = Post::has('comments')-&gt;get();
</code></pre>
<p>也可以指定运算符和数量：</p>
<pre><code>$posts = Post::has('comments', '&gt;=', 3)-&gt;get();
</code></pre>
<p>也可以使用&quot;点号&quot;的形式来获取嵌套的 <code>has</code> 声明：</p>
<pre><code>$posts = Post::has('comments.votes')-&gt;get();
</code></pre>
<p>如果想要更进阶的用法，可以使用 <code>whereHas</code> 和 <code>orWhereHas</code> 方法，在 <code>has</code> 查询里设置 &quot;where&quot; 条件 ：</p>
<pre><code>$posts = Post::whereHas('comments', function($q)
{
    $q-&gt;where('content', 'like', 'foo%');

})-&gt;get();
</code></pre>
<p><a name="dynamic-properties"></a></p>
<h3>动态属性</h3>
<p>Eloquent 可以经由动态属性取得关联对象。 Eloquent 会自动进行关联查询，而且会很聪明的知道应该要使用 <code>get</code>（用在一对多关联）或是 <code>first</code> （用在一对一关联）方法。可以经由和「关联方法名称相同」的动态属性取得对象。例如，如下面的模型对象 <code>$phone</code>：</p>
<pre><code>class Phone extends Model {

    public function user()
    {
        return $this-&gt;belongsTo('App\User');
    }

}

$phone = Phone::find(1);
</code></pre>
<p>您可以不用像下面这样打印用户的 email ：</p>
<pre><code>echo $phone-&gt;user()-&gt;first()-&gt;email;
</code></pre>
<p>而可以简写如下：</p>
<pre><code>echo $phone-&gt;user-&gt;email;
</code></pre>
<blockquote>
<p><strong>注意：</strong> 若取得的是许多关联对象，会返回 <code>Illuminate\Database\Eloquent\Collection</code> 对象。</p>
</blockquote>
<p><a name="eager-loading"></a></p>
<h2>预载入</h2>
<p>预载入是用来减少 N + 1 查询问题。例如，一个 <code>Book</code> 模型数据会关联到一个 <code>Author</code> 。关联会像下面这样定义：</p>
<pre><code>class Book extends Model {

    public function author()
    {
        return $this-&gt;belongsTo('App\Author');
    }

}
</code></pre>
<p>现在考虑下面的代码：</p>
<pre><code>foreach (Book::all() as $book)
{
    echo $book-&gt;author-&gt;name;
}
</code></pre>
<p>上面的循环会执行一次查询取回所有数据库表上的书籍，然而每本书籍都会执行一次查询取得作者。所以若我们有 25 本书，就会进行 26次查询。</p>
<p>很幸运地，我们可以使用预载入大量减少查询次数。使用 <code>with</code> 方法指定想要预载入的关联对象：</p>
<pre><code>foreach (Book::with('author')-&gt;get() as $book)
{
    echo $book-&gt;author-&gt;name;
}
</code></pre>
<p>现在，上面的循环总共只会执行两次查询：</p>
<pre><code>select * from books

select * from authors where id in (1, 2, 3, 4, 5, ...)
</code></pre>
<p>使用预载入可以大大提高程序的性能。</p>
<p>当然，也可以同时载入多种关联：</p>
<pre><code>$books = Book::with('author', 'publisher')-&gt;get();
</code></pre>
<p>甚至可以预载入巢状关联：</p>
<pre><code>$books = Book::with('author.contacts')-&gt;get();
</code></pre>
<p>上面的例子中， <code>author</code> 关联会被预载入， author 的 <code>contacts</code> 关联也会被预载入。</p>
<h3>预载入条件限制</h3>
<p>有时您可能想要预载入关联，同时也想要指定载入时的查询限制。下面有一个例子：</p>
<pre><code>$users = User::with(array('posts' =&gt; function($query)
{
    $query-&gt;where('title', 'like', '%first%');

}))-&gt;get();
</code></pre>
<p>上面的例子里，我们预载入了 user 的 posts 关联，并限制条件为 post 的 title 字段需包含 &quot;first&quot; 。</p>
<p>当然，预载入的闭合函数里不一定只能加上条件限制，也可以加上排序：</p>
<pre><code>$users = User::with(array('posts' =&gt; function($query)
{
    $query-&gt;orderBy('created_at', 'desc');

}))-&gt;get();
</code></pre>
<h3>延迟预载入</h3>
<p>也可以直接从模型的 collection 预载入关联对象。这对于需要根据情况决定是否载入关联对象时，或是跟缓存一起使用时很有用。</p>
<pre><code>$books = Book::all();

$books-&gt;load('author', 'publisher');
</code></pre>
<p>你可以传入一个闭包来对查询构建器进行条件限制：</p>
<pre><code>$books-&gt;load(['author' =&gt; function($query)
{
    $query-&gt;orderBy('published_date', 'asc');
}]);
</code></pre>
<p><a name="inserting-related-models"></a></p>
<h2>新增关联模型</h2>
<h4>附加一个关联模型</h4>
<p>您常常会需要加入新的关联模型。例如新增一个 comment 到 post 。除了手动设定模型的 <code>post_id</code> 外键，也可以从上层的 <code>Post</code> 模型新增关联的 comment ：</p>
<pre><code>$comment = new Comment(array('message' =&gt; 'A new comment.'));

$post = Post::find(1);

$comment = $post-&gt;comments()-&gt;save($comment);
</code></pre>
<p>上面的例子里，新增的 comment 模型中 <code>post_id</code> 字段会被自动设定。</p>
<p>如果想要同时新增很多关联模型：</p>
<pre><code>$comments = array(
    new Comment(array('message' =&gt; 'A new comment.')),
    new Comment(array('message' =&gt; 'Another comment.')),
    new Comment(array('message' =&gt; 'The latest comment.'))
);

$post = Post::find(1);

$post-&gt;comments()-&gt;saveMany($comments);
</code></pre>
<h3>从属关联模型 ( Belongs To )</h3>
<p>要更新 <code>belongsTo</code> 关联时，可以使用 <code>associate</code> 方法。这个方法会设定子模型的外键：</p>
<pre><code>$account = Account::find(10);

$user-&gt;account()-&gt;associate($account);

$user-&gt;save();
</code></pre>
<h3>新增多对多关联模型 ( Many To Many )</h3>
<p>您也可以新增多对多的关联模型。让我们继续使用 <code>User</code> 和 <code>Role</code> 模型作为例子。我们可以使用 <code>attach</code> 方法简单地把 roles 附加给一个 user：</p>
<h4>附加多对多模型</h4>
<pre><code>$user = User::find(1);

$user-&gt;roles()-&gt;attach(1);
</code></pre>
<p>也可以传入要存在枢纽表中的属性数组：</p>
<pre><code>$user-&gt;roles()-&gt;attach(1, array('expires' =&gt; $expires));
</code></pre>
<p>当然，有 <code>attach</code> 方法就会有相反的 <code>detach</code> 方法：</p>
<pre><code>$user-&gt;roles()-&gt;detach(1);
</code></pre>
<p><code>attach</code> 和 <code>detach</code> 都可以接受ID数组作为参数：</p>
<pre><code>$user = User::find(1);

$user-&gt;roles()-&gt;detach([1, 2, 3]);

$user-&gt;roles()-&gt;attach([1 =&gt; ['attribute1' =&gt; 'value1'], 2, 3]);
</code></pre>
<h4>使用 Sync 方法同时附加一个以上多对多关联</h4>
<p>您也可以使用 <code>sync</code> 方法附加关联模型。 <code>sync</code> 方法会把根据 ID 数组把关联存到枢纽表。附加完关联后，枢纽表里的模型只会关联到 ID 数组里的 id ：</p>
<pre><code>$user-&gt;roles()-&gt;sync(array(1, 2, 3));
</code></pre>
<h4>Sync 时在枢纽表加入额外数据</h4>
<p>也可以在把每个 ID 加入枢纽表时，加入其他字段的数据：</p>
<pre><code>$user-&gt;roles()-&gt;sync(array(1 =&gt; array('expires' =&gt; true)));
</code></pre>
<p>有时您可能想要使用一个命令，在建立新模型数据的同时附加关联。可以使用 <code>save</code> 方法达成目的：</p>
<pre><code>$role = new Role(array('name' =&gt; 'Editor'));

User::find(1)-&gt;roles()-&gt;save($role);
</code></pre>
<p>上面的例子里，新的 <code>Role</code> 模型对象会在储存的同时关联到 <code>user</code> 模型。也可以传入属性数组把数据加到关联数据库表：</p>
<pre><code>User::find(1)-&gt;roles()-&gt;save($role, array('expires' =&gt; $expires));
</code></pre>
<p><a name="touching-parent-timestamps"></a></p>
<h2>更新上层时间戳</h2>
<p>当模型 <code>belongsTo</code> 另一个模型时，比方说一个 <code>Comment</code> 属于一个 <code>Post</code> ，如果能在子模型被更新时，更新上层的时间戳，这将会很有用。例如，当 <code>Comment</code> 模型更新时，您可能想要能够同时自动更新 <code>Post</code> 的 <code>updated_at</code> 时间戳。 Eloquent 让事情变得很简单。只要在子关联的类里，把关联方法名称加入 <code>touches</code> 属性即可：</p>
<pre><code>class Comment extends Model {

    protected $touches = array('post');

    public function post()
    {
        return $this-&gt;belongsTo('App\Post');
    }

}
</code></pre>
<p>现在，当您更新 <code>Comment</code> 时，对应的 <code>Post</code> 会自动更新 <code>updated_at</code> 字段：</p>
<pre><code>$comment = Comment::find(1);

$comment-&gt;text = 'Edit to this comment!';

$comment-&gt;save();
</code></pre>
<p><a name="working-with-pivot-tables"></a></p>
<h2>使用枢纽表</h2>
<p>如您所知，要操作多对多关联需要一个中间的数据库表。 Eloquent 提供了一些有用的方法可以和这张表互动。例如，假设 <code>User</code> 对象关联到很多 <code>Role</code> 对象。取出这些关联对象时，我们可以在关联模型上取得 <code>pivot</code> 数据库表的数据：</p>
<pre><code>$user = User::find(1);

foreach ($user-&gt;roles as $role)
{
    echo $role-&gt;pivot-&gt;created_at;
}
</code></pre>
<p>注意我们取出的每个 <code>Role</code> 模型对象会自动给一个 <code>pivot</code> 属性。这属性包含了枢纽表的模型数据，可以像一般的 Eloquent 模型一样使用。</p>
<p>默认 <code>pivot</code> 对象只会有关联键的属性。如果您想让 pivot 可以包含其他枢纽表的字段，可以在定义关联方法时指定那些字段：</p>
<pre><code>return $this-&gt;belongsToMany('App\Role')-&gt;withPivot('foo', 'bar');
</code></pre>
<p>现在可以在 <code>Role</code> 模型的 <code>pivot</code> 对象上取得 <code>foo</code> 和 <code>bar</code> 属性了。</p>
<p>如果您想要可以自动维护枢纽表的 <code>created_at</code> 和 <code>updated_at</code> 时间戳，在定义关联方法时加上 <code>withTimestamps</code> 方法：</p>
<pre><code>return $this-&gt;belongsToMany('App\Role')-&gt;withTimestamps();
</code></pre>
<h4>删除枢纽表的关联数据</h4>
<p>要删除模型在枢纽表的所有关联数据，可以使用 <code>detach</code> 方法：</p>
<pre><code>User::find(1)-&gt;roles()-&gt;detach();
</code></pre>
<p>注意，如上的操作不会移除 <code>roles</code> 数据库表里面的数据，只会移除枢纽表里的关联数据。</p>
<h4>更新枢纽表的数据</h4>
<p>有时您只想更新枢纽表的数据，而没有要移除关联。如果您想更新枢纽表，可以像下面的例子使用 <code>updateExistingPivot</code> 方法：</p>
<pre><code>User::find(1)-&gt;roles()-&gt;updateExistingPivot($roleId, $attributes);
</code></pre>
<h4>自定义枢纽模型</h4>
<p>Laravel 允许您自定义枢纽模型。要自定义模型，首先要建立一个继承 Eloquent 的「基本」模型类。在其他的 Eloquent 模型继承这个自定义的基本类，而不是默认的 Eloquent 。在基本模型类里，加入下面的方法返回自定义的枢纽模型实例：</p>
<pre><code>public function newPivot(Model $parent, array $attributes, $table, $exists)
{
    return new YourCustomPivot($parent, $attributes, $table, $exists);
}
</code></pre>
<p><a name="collections"></a></p>
<h2>集合</h2>
<p>所有 Eloquent 查询返回的数据，如果结果多于一条，不管是经由 <code>get</code> 方法或是 <code>relationship</code>，都会转换成集合对象返回。这个对象实现了 <code>IteratorAggregate</code> PHP 接口，所以可以像数组一般进行遍历。而集合对象本身还拥有很多有用的方法可以操作模型数据。</p>
<h4>确认集合中里是否包含特定键值</h4>
<p>例如，我们可以使用 <code>contains</code> 方法，确认结果数据中，是否包含主键为特定值的对象。</p>
<pre><code>$roles = User::find(1)-&gt;roles;

if ($roles-&gt;contains(2))
{
    //
}
</code></pre>
<p>集合也可以转换成数组或 JSON：</p>
<pre><code>$roles = User::find(1)-&gt;roles-&gt;toArray();

$roles = User::find(1)-&gt;roles-&gt;toJson();
</code></pre>
<p>如果集合被转换成字符串类型，会返回 JSON 格式：</p>
<pre><code>$roles = (string) User::find(1)-&gt;roles;
</code></pre>
<h4>集合遍历</h4>
<p>Eloquent 集合里包含了一些有用的方法可以进行循环或是进行过滤：</p>
<pre><code>$roles = $user-&gt;roles-&gt;each(function($role)
{
    //
});
</code></pre>
<h4>集合过滤</h4>
<p>过滤集合时，回调函数的使用方式和 <a href="(http://php.net/manual/en/function.array-filter.php)">array_filter</a> 里一样。</p>
<pre><code>$users = $users-&gt;filter(function($user)
{
    return $user-&gt;isAdmin();
});
</code></pre>
<blockquote>
<p><strong>注意：</strong> 如果要在过滤集合之后转成 JSON，转换之前先调用 <code>values</code> 方法重设数组的键值。</p>
</blockquote>
<h4>遍历传入集合里的每个对象到回调函数</h4>
<pre><code>$roles = User::find(1)-&gt;roles;

$roles-&gt;each(function($role)
{
    //
});
</code></pre>
<h4>依照属性值排序</h4>
<pre><code>$roles = $roles-&gt;sortBy(function($role)
{
    return $role-&gt;created_at;
});
</code></pre>
<h4>依照属性值排序</h4>
<pre><code>$roles = $roles-&gt;sortBy('created_at');
</code></pre>
<h4>返回自定义的集合对象</h4>
<p>有时您可能想要返回自定义的集合对象，让您可以在集合类里加入想要的方法。可以在 Eloquent 模型类里重写 <code>newCollection</code> 方法：</p>
<pre><code>class User extends Model {

    public function newCollection(array $models = array())
    {
        return new CustomCollection($models);
    }

}
</code></pre>
<p><a name="accessors-and-mutators"></a></p>
<h2>获取器和修改器</h2>
<h4>定义获取器</h4>
<p>Eloquent 提供了一种便利的方法，可以在获取或设定属性时进行转换。要定义获取器，只要在模型里加入类似 <code>getFooAttribute</code> 的方法。注意方法名称应该使用驼峰式大小写命名，而对应的 database 字段名称是下划线分隔小写命名：</p>
<pre><code>class User extends Model {

    public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }

}
</code></pre>
<p>上面的例子中， first_name 字段设定了一个获取器。注意传入方法的参数是原本的字段数据。</p>
<h4>定义修改器</h4>
<p>修改器的定义方式也是类似的：</p>
<pre><code>class User extends Model {

    public function setFirstNameAttribute($value)
    {
        $this-&gt;attributes['first_name'] = strtolower($value);
    }

}
</code></pre>
<p><a name="date-mutators"></a></p>
<h2>日期转换器</h2>
<p>默认 Eloquent 会把 <code>created_at</code> 和 <code>updated_at</code> 字段属性转换成 <a href="https://github.com/briannesbitt/Carbon" target="_blank">Carbon</a> 实例，它提供了很多有用的方法，并继承了 PHP 原生的 <code>DateTime</code> 类。</p>
<p>您可以通过重写模型的 <code>getDates</code> 方法，自定义哪个字段可以被自动转换，或甚至完全关闭这个转换：</p>
<pre><code>public function getDates()
{
    return array('created_at');
}
</code></pre>
<p>当字段是表示日期的时候，可以将值设为 UNIX timestamp 、日期字符串（ Y-m-d ）、 日期时间（ date-time ）字符串，当然还有 <code>DateTime</code> 或 <code>Carbon</code> 实例。</p>
<p>要完全关闭日期转换功能，只要从 <code>getDates</code> 方法返回空数组即可：</p>
<pre><code>public function getDates()
{
    return array();
}
</code></pre>
<p><a name="attribute-casting"></a></p>
<h2>属性类型转换</h2>
<p>如果您想要某些属性始终转换成另一个数据类型, 您可以在模型中增加 <code>casts</code> 属性。否则，您需要为每个属性定义修改器，这样会增加更多的时间开销。这里有一个使用 <code>casts</code> 属性的例子：</p>
<pre><code>/**
 * 需要被转换成基本类型的属性值。
 *
 * @var array
 */
protected $casts = [
    'is_admin' =&gt; 'boolean',
];
</code></pre>
<p>现在当你获取 <code>is_admin</code> 属性时始终会是布尔类型，甚至在数据库中存储的这个值是一个整型也会被转换。其他支持的类型转换值有： <code>integer</code>, <code>real</code>, <code>float</code>, <code>double</code>, <code>string</code>, <code>boolean</code>, <code>object</code> 和 <code>array</code> 。</p>
<p>如果您存储的值是一个序列化的 JSON 时，那么 <code>array</code> 类型转换将会非常有用。比如，您的数据表里有一个 TEXT 类型的字段存储着序列化后的 JSON 数据， 通过增加 <code>array</code> 类型转换, 当获取这个属性的时候会自动反序列化成 PHP 的数组：</p>
<pre><code>/**
 * 需要被转换成基本类型的属性值。
 *
 * @var array
 */
protected $casts = [
    'options' =&gt; 'array',
];
</code></pre>
<p>现在，当你使用 Eloquent 模型时：</p>
<pre><code>$user = User::find(1);

// $options 是一个数组...
$options = $user-&gt;options;

// options 会自动序列化成 JSON...
$user-&gt;options = ['foo' =&gt; 'bar'];
</code></pre>
<p><a name="model-events"></a></p>
<h2>模型事件</h2>
<p>Eloquent 模型有很多事件可以触发，让您可以在模型操作的生命周期的不同时间点，使用下列方法绑定事件： <code>creating</code>, <code>created</code>, <code>updating</code>, <code>updated</code>, <code>saving</code>, <code>saved</code>, <code>deleting</code>, <code>deleted</code>, <code>restoring</code>, <code>restored</code>。</p>
<p>当一个对象初次被储存到数据库， <code>creating</code> 和 <code>created</code> 事件会被触发。如果不是新对象而调用了 <code>save</code> 方法， <code>updating</code> / <code>updated</code> 事件会被触发。而两者的 <code>saving</code> / <code>saved</code> 事件都会被触发。</p>
<h4>使用事件取消数据库操作</h4>
<p>如果 <code>creating</code> 、 <code>updating</code> 、 <code>saving</code> 、 <code>deleting</code> 事件返回 false 的话，就会取消数据库操作</p>
<pre><code>User::creating(function($user)
{
    if ( ! $user-&gt;isValid()) return false;
});
</code></pre>
<h4>注册事件监听者的方式</h4>
<p>您可以在 <code>EventServiceProvider</code> 中注册您的模型事件绑定。比如：</p>
<pre><code>/**
 * Register any other events for your application.
 *
 * @param  \Illuminate\Contracts\Events\Dispatcher  $events
 * @return void
 */
public function boot(DispatcherContract $events)
{
    parent::boot($events);

    User::creating(function($user)
    {
        //
    });
}
</code></pre>
<p><a name="model-observers"></a></p>
<h2>模型观察者</h2>
<p>要整合模型的事件处理，可以注册一个模型观察者。观察者类里要设定对应模型事件的方法。例如，观察者类里可能有 <code>creating</code>、 <code>updating</code> 、 <code>saving</code> 方法，还有其他对应模型事件名称的方法：</p>
<p>例如，一个模型观察者类可能看起来如下：</p>
<pre><code>class UserObserver {

    public function saving($model)
    {
        //
    }

    public function saved($model)
    {
        //
    }

}
</code></pre>
<p>可以使用 <code>observe</code> 方法注册一个观察者实例：</p>
<pre><code>User::observe(new UserObserver);
</code></pre>
<p><a name="model-url-generation"></a></p>
<h2>模型 URL 生成</h2>
<p>当你把一个模型实例传递给 <code>route</code> 或者 <code>action</code> 方法时，模型的主键会被插入到生成的 URI 中。比如：</p>
<pre><code>Route::get('user/{user}', 'UserController@show');

action('UserController@show', [$user]);
</code></pre>
<p>在这个例子中 <code>$user-&gt;id</code> 属性会被插入到生成的 URL 的 <code>{user}</code> 这个占位符中。不过，如果你想使用其他的属性而不是 ID 的话，你可以覆盖模型的 <code>getRouteKey</code> 方法：</p>
<pre><code>public function getRouteKey()
{
    return $this-&gt;slug;
}
</code></pre>
<p><a name="converting-to-arrays-or-json"></a></p>
<h2>转换成数组 / JSON</h2>
<h4>将模型数据转成数组</h4>
<p>当构建 JSON API 时，您可能常常需要把模型和关联对象转换成数组或JSON。所以Eloquent里已经包含了这些方法。要把模型和已载入的关联对象转成数组，可以使用 <code>toArray</code> 方法：</p>
<pre><code>$user = User::with('roles')-&gt;first();

return $user-&gt;toArray();
</code></pre>
<p>注意也可以把整个的模型集合转换成数组：</p>
<pre><code>return User::all()-&gt;toArray();
</code></pre>
<h4>将模型转换成 JSON</h4>
<p>要把模型转换成 JSON，可以使用 <code>toJson</code> 方法：</p>
<pre><code>return User::find(1)-&gt;toJson();
</code></pre>
<h4>从路由中返回模型</h4>
<p>注意当模型或集合被转换成字符串类型时会自动转换成 JSON 格式，这意味着您可以直接从路由返回 Eloquent 对象！</p>
<pre><code>Route::get('users', function()
{
    return User::all();
});
</code></pre>
<h4>转换成数组或 JSON 时隐藏属性</h4>
<p>有时您可能想要限制能出现在数组或 JSON 格式的属性数据，比如密码字段。只要在模型里增加 <code>hidden</code> 属性即可</p>
<pre><code>class User extends Model {

    protected $hidden = array('password');

}
</code></pre>
<blockquote>
<p><strong>注意：</strong> 要隐藏关联数据，要使用关联的<em>方法</em>名称，而不是动态获取的属性名称。</p>
</blockquote>
<p>此外，可以使用 <code>visible</code> 属性定义白名单：</p>
<pre><code>protected $visible = array('first_name', 'last_name');
</code></pre>
<p><a name="array-appends"></a>
有时候您可能想要增加不存在数据库字段的属性数据。这时候只要定义一个获取器即可：</p>
<pre><code>public function getIsAdminAttribute()
{
    return $this-&gt;attributes['admin'] == 'yes';
}
</code></pre>
<p>定义好获取器之后，再把对应的属性名称加到模型里的 <code>appends</code> 属性：</p>
<pre><code>protected $appends = array('is_admin');
</code></pre>
<p>把属性加到 <code>appends</code> 数组之后，在模型数据转换成数组或 JSON 格式时就会有对应的值。在 <code>appends</code> 数组中定义的值同样遵循模型中 <code>visible</code> 和 <code>hidden</code> 的设定。</p>

		</article>
	</div>


	<footer class="main">
		<ul>
			<li class="nav-docs"><a href="../index.html">中文文档</a></li>
			<li class="nav-community"><a href="http://wenda.golaravel.com" target="_blank">问答社区</a></li>
			<li class="nav-api"><a href="http://laravel.com/api/5.0/" target="_blank">API</a></li>
			<li class="nav-laracasts"><a href="https://laracasts.com" target="_blank">视频教程（国外）</a></li>
			<li class="nav-forge"><a href="https://forge.laravel.com" target="_blank">Forge</a></li>
			
		</ul>
		<p>Laravel is a trademark of Taylor Otwell. Copyright &copy; Taylor Otwell.</p>
		<p class="less-significant"><a href="http://jackmcdade.com" target="_blank">Design by Jack McDade</a></p>
	</footer>

	<script src="../../assets/js/laravel.js"></script>
	<script src="../../assets/js/viewport-units-buggyfill.js"></script>
	<script>window.viewportUnitsBuggyfill.init();</script>
	<script type="text/javascript">
	var _bdhmProtocol = (("https:" == document.location.protocol) ? " https://" : " http://");
	document.write(unescape("%3Cscript src='" + _bdhmProtocol + "hm.baidu.com/h.js%3Fc8d13872a523d9c286aa7affbe0921f1' type='text/javascript'%3E%3C/script%3E"));
	</script>
</body>
</html>
